(function() {
  const canvas = document.getElementById('heartCanvas');
  const ctx = canvas.getContext('2d');

  let W = 0, H = 0, DPR = Math.max(1, window.devicePixelRatio || 1);
  function resize() {
    W = window.innerWidth;
    H = window.innerHeight;
    canvas.width = Math.floor(W * DPR);
    canvas.height = Math.floor(H * DPR);
    canvas.style.width = W + 'px';
    canvas.style.height = H + 'px';
    ctx.setTransform(DPR,0,0,DPR,0,0);
  }
  window.addEventListener('resize', resize, {passive:true});
  resize();

  // 心形参数方程 (x,y) scaled later
  function heartPoint(t) {
    const x = 16 * Math.pow(Math.sin(t), 3);
    const y = 13 * Math.cos(t) - 5 * Math.cos(2*t) - 2 * Math.cos(3*t) - Math.cos(4*t);
    return {x, y};
  }

  // 采样心形路径点，居中并放大
  const heartSamples = [];
  function buildHeartSamples() {
    heartSamples.length = 0;
    const samples = 240;
    for (let i = 0; i < samples; i++) {
      const t = (i / samples) * Math.PI * 2;
      const p = heartPoint(t);
      heartSamples.push(p);
    }
  }
  buildHeartSamples();

  // 将心形坐标映射到画布位置
  function heartToCanvas(p, scale, cx, cy) {
    return {
      x: cx + p.x * scale,
      y: cy - p.y * scale
    };
  }

  // 粒子结构
  class Particle {
    constructor(x,y, vx, vy, size, life, color, glow) {
      this.x = x; this.y = y;
      this.vx = vx; this.vy = vy;
      this.size = size;
      this.life = life; this.maxLife = life;
      this.color = color;
      this.glow = glow || 12;
      this.alpha = 1;
    }
    update(dt) {
      this.x += this.vx * dt;
      this.y += this.vy * dt;
      // slight gravity/drag
      this.vx *= 0.998;
      this.vy *= 0.998;
      this.life -= dt;
      this.alpha = Math.max(0, this.life / this.maxLife);
    }
    draw(ctx) {
      ctx.save();
      ctx.globalCompositeOperation = 'lighter';
      ctx.beginPath();
      const g = ctx.createRadialGradient(this.x, this.y, 0, this.x, this.y, this.size*2);
      g.addColorStop(0, this.color.replace('ALPHA', (0.95*this.alpha).toFixed(2)));
      g.addColorStop(0.4, this.color.replace('ALPHA', (0.5*this.alpha).toFixed(2)));
      g.addColorStop(1, this.color.replace('ALPHA', '0'));
      ctx.fillStyle = g;
      ctx.arc(this.x, this.y, this.size, 0, Math.PI*2);
      ctx.fill();
      ctx.restore();
    }
  }

  // 粒子池
  const particles = [];

  // 配色（蓝色系，使用 rgba with ALPHA placeholder）
  const blueVariants = [
    'rgba(170,230,255,ALPHA)', // 浅冰蓝
    'rgba(120,200,255,ALPHA)',
    'rgba(80,160,255,ALPHA)',
    'rgba(140,220,255,ALPHA)'
  ];

  // 生成单个心形轮廓粒子：根据心形样本索引选择位置并给出外散速度
  function spawnHeartParticle() {
    // center and scale based on min(screen)
    const cx = W/2;
    const cy = H/2 - Math.min(H, W) * 0.06; // 微调偏上
    const baseScale = Math.min(W, H) / 64; // 适配各种屏幕
    // 随机取 sample 并稍微抖动
    const s = heartSamples[Math.floor(Math.random() * heartSamples.length)];
    const pcoord = heartToCanvas(s, baseScale, cx, cy);
    // outward velocity from heart center
    const dx = pcoord.x - cx;
    const dy = pcoord.y - cy;
    const dist = Math.sqrt(dx*dx + dy*dy) || 1;
    const normx = dx / dist, normy = dy / dist;
    // spawn slightly inside/outside
    const jitter = (Math.random() - 0.5) * 6;
    const x = pcoord.x + jitter;
    const y = pcoord.y + jitter;
    const speed = 10 + Math.random()*40;
    const vx = normx * (0.3*speed) + (Math.random()-0.5)*12;
    const vy = normy * (0.3*speed) + (Math.random()-0.5)*12;
    const size = 1 + Math.random()*3;
    const life = 3 + Math.random()*3;
    const color = blueVariants[Math.floor(Math.random()*blueVariants.length)];
    particles.push(new Particle(x, y, vx, vy, size, life, color));
  }

  // 生成散射小星点，制造闪烁感
  function spawnSparkle() {
    const cx = W/2;
    const cy = H/2 - Math.min(H, W) * 0.06;
    const baseScale = Math.min(W, H) / 64;
    // pick random sample and move a bit further
    const s = heartSamples[Math.floor(Math.random()*heartSamples.length)];
    const pcoord = heartToCanvas(s, baseScale, cx, cy);
    // random outward offset
    const angle = Math.random()*Math.PI*2;
    const radius = 6 + Math.random()*40;
    const x = pcoord.x + Math.cos(angle) * radius;
    const y = pcoord.y + Math.sin(angle) * radius * 0.7;
    const vx = (Math.random()-0.5) * 10;
    const vy = (Math.random()-0.5) * 10 - 8 * Math.random(); // slight upward drift
    const size = 0.6 + Math.random()*2.2;
    const life = 2 + Math.random()*2;
    const color = blueVariants[Math.floor(Math.random()*blueVariants.length)];
    particles.push(new Particle(x, y, vx, vy, size, life, color));
  }

  // 控制生成频率：每秒 4 个 heartParticle + 一些 sparkles
  let lastTime = performance.now();
  let accum = 0;
  const heartPerSec = 4; // 每秒生成数
  const sparklePerSec = 8;

  function step(now) {
    const dtSec = Math.min(0.05, (now - lastTime) / 1000);
    lastTime = now;

    accum += dtSec;

    // spawn based on elapsed time
    const toSpawnHeart = Math.floor(accum * heartPerSec);
    const toSpawnSpark = Math.floor(accum * sparklePerSec);
    // reduce accum by used fraction
    if (toSpawnHeart > 0 || toSpawnSpark > 0) {
      accum -= (toSpawnHeart / heartPerSec) + (toSpawnSpark / sparklePerSec);
    }

    for (let i=0;i<toSpawnHeart;i++) spawnHeartParticle();
    for (let i=0;i<toSpawnSpark;i++) spawnSparkle();

    // also occasional burst near center
    if (Math.random() < 0.02) {
      for (let i=0;i<6;i++) spawnSparkle();
    }

    // update
    for (let i = particles.length - 1; i >= 0; i--) {
      const p = particles[i];
      p.update(dtSec);
      if (p.life <= 0 || p.alpha <= 0.01) {
        particles.splice(i,1);
      }
    }

    // draw
    ctx.clearRect(0,0,W,H);

    // subtle background glow: draw a soft blue radial glow at center
    const cx = W/2, cy = H/2 - Math.min(H,W)*0.06;
    const glowGrad = ctx.createRadialGradient(cx, cy, 20, cx, cy, Math.min(W,H)*0.9);
    glowGrad.addColorStop(0, 'rgba(8,30,80,0.12)');
    glowGrad.addColorStop(0.25, 'rgba(8,30,80,0.06)');
    glowGrad.addColorStop(1, 'rgba(0,0,0,0)');
    ctx.fillStyle = glowGrad;
    ctx.fillRect(0,0,W,H);

    // draw each particle (lighter composite to make glow)
    for (let i=0;i<particles.length;i++) {
      particles[i].draw(ctx);
    }

    // optional: draw a faint heart outline using stroked path for structure
    ctx.save();
    ctx.globalAlpha = 0.15;
    ctx.lineWidth = Math.max(0.8, Math.min(W,H)/300);
    ctx.strokeStyle = 'rgba(120,180,255,0.6)';
    ctx.beginPath();
    const scale = Math.min(W,H)/64;
    for (let i=0;i<heartSamples.length;i++) {
      const p = heartToCanvas(heartSamples[i], scale, cx, cy);
      if (i === 0) ctx.moveTo(p.x, p.y);
      else ctx.lineTo(p.x, p.y);
    }
    ctx.closePath();
    ctx.stroke();
    ctx.restore();

    requestAnimationFrame(step);
  }

  // init
  resize();
  lastTime = performance.now();
  // prefill some particles
  for (let i=0;i<40;i++) {
    spawnSparkle();
  }
  requestAnimationFrame(step);

  // optional: press Esc to clear
  window.addEventListener('keydown', function(e){
    if (e.key === 'Escape') {
      particles.length = 0;
      ctx.clearRect(0,0,W,H);
    }
  }, false);
})();
